#include "SystemSDL_1_3.h"

#include <SDL.h>
#include <cstdarg>

#define PRINT_ENUM( X )         \
case X:                         \
    SysMessageCat( "\t" );      \
    SysMessageCat( #X );        \
    break;

//
//  EXTERNS
//
extern SDL_RWops* g_stdout;
extern SDL_RWops* g_stderr;

const int MAX_ARRAY_LENGTH                                      = 1028;
char CSystemSDL_1_3_Helper::m_SysMessage[MAX_ARRAY_LENGTH]      = { '\0' };
char CSystemSDL_1_3_Helper::m_SysErrorMessage[MAX_ARRAY_LENGTH] = { '\0' };
char* CSystemSDL_1_3_Helper::m_pCurrentPos                      = &m_SysMessage[0];

void CSystemHelper::SetError( const char* error, ... )
{
    if( SDL_strlen( m_SysErrorMessage ) > 0 )
        SDL_memset( m_SysErrorMessage, '\0', sizeof( char ) * MAX_ARRAY_LENGTH );

    va_list ap;

    va_start( ap, error );
    SDL_vsnprintf( m_SysErrorMessage, MAX_ARRAY_LENGTH, error, ap );
    va_end( ap );

    SDL_assert( &m_SysErrorMessage[0] );

    int len = SDL_strlen( m_SysErrorMessage );
    const char* err = SDL_GetError();

    if( SDL_strlen( err ) != 0 )
        SDL_snprintf(m_SysErrorMessage + len, MAX_ARRAY_LENGTH - len,
                     "SDL says: %s\n", SDL_GetError() );
}

void CSystemSDL_1_3_Helper::ResetSysMessage( void )
{
    SDL_assert( m_pCurrentPos );

    while( m_pCurrentPos != &m_SysMessage[0] )
    {
        SDL_assert( m_pCurrentPos-- );
        *m_pCurrentPos = '\0';
    }
}

void CSystemSDL_1_3_Helper::SysMessageCat( const char* msg )
{
    int len = SDL_strlen( msg );
    int rem_len = &m_SysMessage[MAX_ARRAY_LENGTH] - m_pCurrentPos;

    SDL_strlcat( m_pCurrentPos, msg, rem_len );

    if( len >= rem_len )
        m_pCurrentPos = &m_SysMessage[MAX_ARRAY_LENGTH];

    m_pCurrentPos = &m_SysMessage[len];
}

void CSystemSDL_1_3_Helper::PrintRenderer( SDL_Renderer* rnd )
{
    SDL_assert( rnd );

    SDL_RendererInfo info;

    SDL_GetRendererInfo( rnd, &info );

    const char* name                = info.name;
    int max_tex_height              = info.max_texture_height;
    int max_tex_width               = info.max_texture_width;
    unsigned int num_tex_formats    = info.num_texture_formats;
    const unsigned int* tex_formats = info.texture_formats;

    int len = SDL_strlen( m_SysMessage );
    SDL_snprintf( m_SysMessage + len, MAX_ARRAY_LENGTH - len,
                  "RendererInfo\n\tName: %s\n\tTex_Height: %d\n\tTex_Width: %d\n\t"
                   "Supported Formats: %d\n", name, max_tex_height, max_tex_width,
                   num_tex_formats );

    ResetSysMessage();

    for( unsigned int index = 0; index < num_tex_formats; ++index )
        PixelFormatToString( tex_formats[index] );

    SDL_RWwrite( g_stdout, m_SysMessage, sizeof( char ), SDL_strlen( m_SysMessage ) );
}

void CSystemSDL_1_3_Helper::PrintSurface( SDL_Surface* surf )
{
    SDL_assert( surf );

    const SDL_PixelFormat* fmt = surf->format;
    int width = surf->w;
    int height = surf->h;
    int pitch = surf->pitch;

    ResetSysMessage();

    printf( "SDL_Surface\n\tSupported Pixel format: \n%s\tDeminsions %dx%d\n\tPitch: %d\n",
            PixelFormatToString( fmt->format ), width, height, pitch );
}

void CSystemSDL_1_3_Helper::PrintEvent( const SDL_Event& ev )
{
    ResetSysMessage();

    SysMessageCat( "Event Detected: " );

    // grab the correct in focus window
    int id = ev.window.windowID;
    SDL_Window* wnd = SDL_GetWindowFromID( id );

    SDL_assert( wnd );

    int buffer_length = 255;
    char buffer[buffer_length];

    SDL_snprintf( buffer, MAX_ARRAY_LENGTH - buffer_length, "ID: %d, %s", id, SDL_GetWindowTitle( wnd ) );
    SysMessageCat( buffer );
    SDL_memset( &buffer, '\0', sizeof( char ) * buffer_length );

    if( ev.type == SDL_WINDOWEVENT )
    {
        EventFlagToString( ev.window.event );
        printf( "%s\n", m_SysMessage );
    }

    else
    {
        switch( ev.type )
        {
        case SDL_KEYDOWN:
        case SDL_KEYUP:
            {
                EventFlagToString( ev.type );
                SDL_snprintf( buffer, buffer_length, "\t%s is ", SDL_GetScancodeName( ev.key.keysym.scancode ) );
                SysMessageCat( buffer );
                IsPressedToString( ev.key.state );
                printf( "%s\n", m_SysMessage );
                break;
            }
        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONUP:
            {
                EventFlagToString( ev.type );
                MouseButtonToString( ev.button.button );
                SysMessageCat( " is " );
                IsPressedToString( ev.button.state );
                SDL_snprintf( buffer, buffer_length, " @ %dx%d", ev.button.x, ev.button.y );
                SysMessageCat( buffer );
                printf( "%s\n", m_SysMessage );
                break;
            }
        default:
            {
                //SysMessageCat( "Unknown Event" );
                //printf( "%s\n", m_SysMessage );
                break;
            }
        }
    }

    ResetSysMessage();
}

const char* CSystemSDL_1_3_Helper::PixelFormatToString( const unsigned int flags )
{
    switch( flags )
    {
        PRINT_ENUM( SDL_PIXELFORMAT_ABGR1555 )
        PRINT_ENUM( SDL_PIXELFORMAT_ABGR4444 )
        PRINT_ENUM( SDL_PIXELFORMAT_ABGR8888 )
        PRINT_ENUM( SDL_PIXELFORMAT_ARGB1555 )
        PRINT_ENUM( SDL_PIXELFORMAT_ARGB2101010 )
        PRINT_ENUM( SDL_PIXELFORMAT_ARGB4444 )
        PRINT_ENUM( SDL_PIXELFORMAT_ARGB8888 )
        PRINT_ENUM( SDL_PIXELFORMAT_BGR24 )
        PRINT_ENUM( SDL_PIXELFORMAT_BGR555 )
        PRINT_ENUM( SDL_PIXELFORMAT_BGR565 )
        PRINT_ENUM( SDL_PIXELFORMAT_BGR888 )
        PRINT_ENUM( SDL_PIXELFORMAT_BGRA4444 )
        PRINT_ENUM( SDL_PIXELFORMAT_BGRA5551 )
        PRINT_ENUM( SDL_PIXELFORMAT_BGRA8888 )
        PRINT_ENUM( SDL_PIXELFORMAT_INDEX1LSB )
        PRINT_ENUM( SDL_PIXELFORMAT_INDEX1MSB )
        PRINT_ENUM( SDL_PIXELFORMAT_INDEX4LSB )
        PRINT_ENUM( SDL_PIXELFORMAT_INDEX4MSB )
        PRINT_ENUM( SDL_PIXELFORMAT_INDEX8 )
        PRINT_ENUM( SDL_PIXELFORMAT_IYUV )
        PRINT_ENUM( SDL_PIXELFORMAT_RGB24 )
        PRINT_ENUM( SDL_PIXELFORMAT_RGB332 )
        PRINT_ENUM( SDL_PIXELFORMAT_RGB444 )
        PRINT_ENUM( SDL_PIXELFORMAT_RGB555 )
        PRINT_ENUM( SDL_PIXELFORMAT_RGB565 )
        PRINT_ENUM( SDL_PIXELFORMAT_RGB888 )
        PRINT_ENUM( SDL_PIXELFORMAT_RGBA4444 )
        PRINT_ENUM( SDL_PIXELFORMAT_RGBA5551 )
        PRINT_ENUM( SDL_PIXELFORMAT_RGBA8888 )
        PRINT_ENUM( SDL_PIXELFORMAT_UYVY )
        PRINT_ENUM( SDL_PIXELFORMAT_YUY2 )
        PRINT_ENUM( SDL_PIXELFORMAT_YV12 )
        PRINT_ENUM( SDL_PIXELFORMAT_YVYU )
        PRINT_ENUM( SDL_PIXELFORMAT_UNKNOWN )
        default: break;
    }

    return m_SysMessage;
}

const char* CSystemSDL_1_3_Helper::EventFlagToString( const unsigned int flags )
{
    switch( flags )
    {
        //
        //  WINDOW EVENTS
        //
        PRINT_ENUM( SDL_WINDOWEVENT_CLOSE )
        PRINT_ENUM( SDL_WINDOWEVENT_ENTER )
        PRINT_ENUM( SDL_WINDOWEVENT_EXPOSED )
        PRINT_ENUM( SDL_WINDOWEVENT_FOCUS_GAINED )
        PRINT_ENUM( SDL_WINDOWEVENT_FOCUS_LOST )
        PRINT_ENUM( SDL_WINDOWEVENT_HIDDEN )
        PRINT_ENUM( SDL_WINDOWEVENT_LEAVE )
        PRINT_ENUM( SDL_WINDOWEVENT_MAXIMIZED )
        PRINT_ENUM( SDL_WINDOWEVENT_MINIMIZED )
        PRINT_ENUM( SDL_WINDOWEVENT_MOVED )
        PRINT_ENUM( SDL_WINDOWEVENT_RESIZED )
        PRINT_ENUM( SDL_WINDOWEVENT_RESTORED )
        PRINT_ENUM( SDL_WINDOWEVENT_SHOWN )
        PRINT_ENUM( SDL_WINDOWEVENT_SIZE_CHANGED )
        PRINT_ENUM( SDL_WINDOWEVENT_NONE )    // not used
        //
        //  KEY EVENTS
        //
        PRINT_ENUM( SDL_KEYDOWN )
        PRINT_ENUM( SDL_KEYUP )
        PRINT_ENUM( SDL_TEXTEDITING )
        PRINT_ENUM( SDL_TEXTINPUT )
        //
        //  MOUSE EVENTS
        //
        PRINT_ENUM( SDL_MOUSEMOTION )
        PRINT_ENUM( SDL_MOUSEBUTTONDOWN )
        PRINT_ENUM( SDL_MOUSEBUTTONUP )
        PRINT_ENUM( SDL_MOUSEWHEEL )
        //
        //  TABLET OR MULTIPLE MICE EVENTS
        //
        PRINT_ENUM( SDL_INPUTMOTION )
        PRINT_ENUM( SDL_INPUTBUTTONDOWN )
        PRINT_ENUM( SDL_INPUTBUTTONUP )
        PRINT_ENUM( SDL_INPUTWHEEL )
        PRINT_ENUM( SDL_INPUTPROXIMITYIN )
        PRINT_ENUM( SDL_INPUTPROXIMITYOUT )
        //
        //  JOYSTICK EVENTS
        //
        PRINT_ENUM( SDL_JOYAXISMOTION )
        PRINT_ENUM( SDL_JOYBALLMOTION )
        PRINT_ENUM( SDL_JOYBUTTONDOWN )
        PRINT_ENUM( SDL_JOYBUTTONUP )
        PRINT_ENUM( SDL_JOYHATMOTION )
        //
        //  TOUCH EVENTS
        //
        PRINT_ENUM( SDL_FINGERDOWN )
        PRINT_ENUM( SDL_FINGERUP )
        PRINT_ENUM( SDL_FINGERMOTION )
        PRINT_ENUM( SDL_TOUCHBUTTONDOWN )
        PRINT_ENUM( SDL_TOUCHBUTTONUP )
        //
        //  GESTURE EVENTS
        //
        PRINT_ENUM( SDL_DOLLARGESTURE )
        PRINT_ENUM( SDL_DOLLARRECORD )
        PRINT_ENUM( SDL_MULTIGESTURE )
        //
        //  CLIPBOARD EVENTS
        //
        PRINT_ENUM( SDL_CLIPBOARDUPDATE )
        //
        //  QUIT EVENTS
        //
        PRINT_ENUM( SDL_QUIT )
        default: break;
    }

    return m_SysMessage;
}

const char* CSystemSDL_1_3_Helper::MouseButtonToString( const unsigned int flags )
{
    switch( flags )
    {
    case SDL_BUTTON_LEFT:
        {
            SysMessageCat( "\tBUTTON_LEFT" );
            break;
        }
    case SDL_BUTTON_MIDDLE:
        {
            SysMessageCat( "\tBUTTON_MIDDLE" );
            break;
        }
    case SDL_BUTTON_RIGHT:
        {
            SysMessageCat( "\tBUTTON_RIGHT" );
            break;
        }
    case SDL_BUTTON_X1:
        {
            SysMessageCat( "\tBUTTON_X1");
            break;
        }
    case SDL_BUTTON_X2:
        {
            SysMessageCat( "\tBUTTON_X2" );
            break;
        }
    default:
        {
            SysMessageCat( "\tUNKNOWN MOUSE BUTTON EVENT" );
            break;
        }
    }

    return m_SysMessage;
}

const char* CSystemSDL_1_3_Helper::IsPressedToString( const unsigned int flags )
{
    if( flags )
        SysMessageCat( " down" );
    else
        SysMessageCat( " up" );

    return m_SysMessage;
}

